package cn.trigram.validation.annotation.validator;

import cn.trigram.lang.BigMathUtil;
import cn.trigram.validation.annotation.OracleNumberCheck;
import com.google.common.base.Preconditions;
import java.math.BigDecimal;
import javax.validation.ConstraintValidatorContext;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;

/**
 * OracleNumber校验器父类
 *
 * @version 1.0.0
 * @date 2022/08/04 09:11:06
 * @since 1.0.0
 */
public abstract class AbstractOracleNumberValidator {
    protected OracleNumberCheck oracleNumberCheck;

    protected void initialize(OracleNumberCheck oracleNumberCheck) {
        this.oracleNumberCheck = oracleNumberCheck;
    }

    /**
     * 以下逻辑是为了得到允许精度内最大的数字
     *
     * @param oracleNumberCheck
     * @return {@link BigDecimal }
     */
    protected BigDecimal getMax(OracleNumberCheck oracleNumberCheck) {
        //  对应Oracle Number 类型的第一个参数
        final int precision = oracleNumberCheck.precision();
        Preconditions.checkArgument(precision > 0, "precision必须大于0");
        //  对应Oracle Number 类型的第二个参数
        final int scale = oracleNumberCheck.scale();
        Preconditions.checkArgument(scale >= -84 && scale <= 127, "scale必须在[-84,127]之间");
        BigDecimal max;
        String temp;
        /*
        以下逻辑是为了得到允许精度内最大的数字
        */
        if (precision > scale) {
            if (scale >= 0) {
                // 整数位数= precision -scale
                // 小数位数= scale
                temp = String.format("%s.%s", StringUtils.repeat('9', precision - scale), StringUtils.repeat('9', scale));
            } else {
                // 此时整数位数低位有0的部分
                // 高位非0部分= precision
                // 低位0部分= |scale|
                temp = String.format("%s%s", StringUtils.repeat('9', precision), StringUtils.repeat('0', Math.abs(scale)));
            }
        } else {
            // 只有小数
            // scale 大于 precision 时，precision 表示小数点后第 scale 位向左最多可以有多少位数字
            temp = String.format("0.%s%s", StringUtils.repeat('0', scale - precision), StringUtils.repeat('9', precision));
        }
        max = new BigDecimal(temp);
        return max;
    }


    protected boolean isValid(ConstraintValidatorContext context, BigDecimal fieldValue) {
        BigDecimal max = getMax(oracleNumberCheck);
        boolean b = BigMathUtil.lte(BigMathUtil.abs(fieldValue), max);
        if (!b) {
            final HibernateConstraintValidatorContext hibernateConstraintValidatorContext = context.unwrap(HibernateConstraintValidatorContext.class);
            hibernateConstraintValidatorContext.disableDefaultConstraintViolation();
            hibernateConstraintValidatorContext
                    .addMessageParameter("maxLimit", max.toPlainString())
                    .addMessageParameter("validatedValue", fieldValue.toPlainString())
                    .buildConstraintViolationWithTemplate(oracleNumberCheck.message())
                    .addConstraintViolation();
            return false;
        }
        // 在大小满足的情况下判断小数位精度
        final int precision = oracleNumberCheck.precision();
        final int scale = oracleNumberCheck.scale();
        int realScale = scale;
        if (precision > scale && scale < 0) {
            // 只有这个是特殊的，scale此时不直接表示小数位
            realScale = 0;
        }

        b = fieldValue.stripTrailingZeros().scale() > realScale;
        if (b) {
            final HibernateConstraintValidatorContext hibernateConstraintValidatorContext = context.unwrap(HibernateConstraintValidatorContext.class);
            hibernateConstraintValidatorContext.disableDefaultConstraintViolation();
            hibernateConstraintValidatorContext
                    .addMessageParameter("maxScale", realScale)
                    .addMessageParameter("validatedValue", fieldValue.toPlainString())
                    .buildConstraintViolationWithTemplate("{gf.util.validation.OracleNumberCheck_Scale.message}")
                    .addConstraintViolation();
        }
        return !b;
    }
}